import React from 'react'

const EMPTY: unique symbol = Symbol()

export interface ContainerProviderProps<State = void> {
	initialState?: State
	children: React.ReactNode
}

export interface Container<Value, State = void> {
	Provider: React.ComponentType<ContainerProviderProps<State>>
	useContainer: () => Value
}
/**
 * ## Example
 * ```ts
 * import React, { useState } from "react"
 * import { createContainer } from "@/hooks/unstated"
 *
 * function useCounter(initialState = 0) {
 *   let [count, setCount] = useState(initialState)
 *   let decrement = () => setCount(count - 1)
 *   let increment = () => setCount(count + 1)
 *   return { count, decrement, increment }
 * }
 *
 * let Counter = createContainer(useCounter)
 *
 * function CounterDisplay() {
 *   let counter = Counter.useContainer()
 *   return (
 *     <div>
 *       <button onClick={counter.decrement}>-</button>
 *       <span>{counter.count}</span>
 *       <button onClick={counter.increment}>+</button>
 *     </div>
 *   )
 * }
 *
 * function App() {
 *   return (
 *     <Counter.Provider>
 *       <CounterDisplay />
 *       <Counter.Provider initialState={2}>
 *         <div>
 *             <CounterDisplay />
 *         </div>
 *       </Counter.Provider>
 *     </Counter.Provider>
 *   )
 * }
 *
 * ```
 */
export function createContainer<Value, State = void>(
	useHook: (initialState?: State) => Value
): Container<Value, State> {
	let Context = React.createContext<Value | typeof EMPTY>(EMPTY)

	function Provider(props: ContainerProviderProps<State>) {
		let value = useHook(props.initialState)
		return <Context.Provider value={value}>{props.children}</Context.Provider>
	}

	function useContainer(): Value {
		let value = React.useContext(Context)
		if (value === EMPTY) {
			throw new Error('Component must be wrapped with <Container.Provider>')
		}
		return value
	}

	return { Provider, useContainer }
}

export function useContainer<Value, State = void>(container: Container<Value, State>): Value {
	return container.useContainer()
}
